package cn.mesmile.es.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.mesmile.es.common.R;
import cn.mesmile.es.dao.BookRepository;
import cn.mesmile.es.entity.Author;
import cn.mesmile.es.entity.Book;
import lombok.AllArgsConstructor;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.*;

import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;

/**
 * @author zb
 * @date 2021/6/6 00:23
 * @Description:
 */
@AllArgsConstructor
@RequestMapping("/api/v1/test")
@RestController
public class RepositoryController {

    /**
     * RestHighLevelClient          强大更灵活     不能友好的对象操作
     * ElasticSearchRepository      对象操作友好
     */
    private final ElasticsearchTemplate elasticsearchTemplate;

    private final BookRepository bookRepository;

    private final RestHighLevelClient restHighLevelClient;


    @GetMapping("/save")
    public R save(){
        //  没有无参构造函数,导致rest  序列化失败
        Author author = new Author();
        author.setAuthorDescription("这是一段描述事情");
        author.setIsAuthorPublic(true);
        Book book = Book.builder().author(author)
                .description("书本的描述,三国杀").id(1L)
                .name("红楼梦").pageSize(100).publishDate(new Date()).isPublic(true).build();
        // 保持或者更新，id在es中存在就更新，id在es中不存在则添加
        // 会自动根据实体注解 去创建索引和mapping映射
        Book save = bookRepository.save(book);
        return R.data(save);
    }

    @GetMapping("/saveAll")
    public R saveAll() {
        List<Book> books = new ArrayList<>(500000);
        for (long i = 1500000; i <= 2000000 ; i++) {
            Author author = new Author();
            author.setAuthorDescription("这是一段描述事情事实选项三国");
            author.setIsAuthorPublic(true);
            Book book = Book.builder().author(author)
                    .description("三国演义测试,三国杀File："+ i).id(i)
                    .name("三国演义").pageSize(RandomUtil.randomInt(1,100000)).publishDate(DateUtil.offsetDay(new Date(),RandomUtil.randomInt(-1000,1000))).isPublic(true).build();
            books.add(book);
        }
        bookRepository.saveAll(books);
        return R.data("success");
    }



    @GetMapping("/findOne/{id}")
    public R findOne(@PathVariable("id")Long id) {
        Optional<Book> byId = bookRepository.findById(id);
        Book book = byId.orElse(new Book());
        System.out.println("book = " + book);
        return R.data(book);
    }

    @GetMapping("/count")
    public R count() {
        long count = bookRepository.count();
        return R.data(count);
    }


    @GetMapping("/query")
    public R query() {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder bool = QueryBuilders.boolQuery();
        // "must"与  "should"或  "must_not"非 “filter”  过滤条件
        // 注意这里不要使用没有设置分词的字段作为模糊查询的字段
        // 匹配查询                           需要查询的字段   匹配值
        bool.must(QueryBuilders.matchQuery("description", "三国"));

        // 过滤查询  【单从性能上来说：过滤(filter) 比 直接查询(query) 快】
        List<QueryBuilder> filter = bool.filter();
        // 添加过滤条件                       筛选年龄大于等于1和小于等于200的
        filter.add(QueryBuilders.rangeQuery("pageSize").gte(1).lte(1000));

        queryBuilder.withQuery(bool);
        // 按照年龄升序排列
        queryBuilder.withSort(SortBuilders.fieldSort("pageSize").order(SortOrder.DESC));
        // 分页查询                             页数从0开始，每页查询8条
        queryBuilder.withPageable(PageRequest.of(10, 20));
        // 过滤字段。此处只保留 username和age两个字段
//        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"username","age"},null));
        // 构造查询条件
        NativeSearchQuery build = queryBuilder.build();
        // 分页查询
        long startTime = System.currentTimeMillis();
        Page<Book> page = bookRepository.search(build);
        long endTime = System.currentTimeMillis();
        System.out.println("测试："+ (endTime - startTime) );
        System.out.println("总页数："+page.getTotalPages());
        System.out.println("总数据条数："+page.getTotalElements());
        List<Book> content = page.getContent();
        return R.data(content);
    }

    public void testNativeSearchQueryBuilder () {

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        BoolQueryBuilder bool = QueryBuilders.boolQuery();

        // "must"与  "should"或  "must_not"非 “filter”  过滤条件
        // 注意这里不要使用没有设置分词的字段作为模糊查询的字段
        // 匹配查询                           需要查询的字段   匹配值
        bool.must(QueryBuilders.matchQuery("intro", "测试"));

        // 过滤查询  【单从性能上来说：过滤(filter) 比 直接查询(query) 快】
        List<QueryBuilder> filter = bool.filter();
        // 添加过滤条件                       筛选年龄大于等于6和小于等于30的
        filter.add(QueryBuilders.rangeQuery("age").gte(6).lte(30));
        // 以【关键词】类型的 前缀查找
        QueryBuilders.prefixQuery("intro", "测");
        // 使用【关键词】查询
        QueryBuilders.termQuery("intro", "测试");
        // 使用【关键词】查询 或的语义
        QueryBuilders.termsQuery("intro","测试", "test");
        // 匹配查询所有
        QueryBuilders.matchAllQuery();
        // 匹配【关键词】查询  ? 代表匹配一个词     * 匹配0到任意多个  【一般把 ? 和 * 放在词的后面】
        QueryBuilders.wildcardQuery("intro", "测试?");
        // 多id查询
        QueryBuilders.idsQuery().addIds("id1","id2","id3");
        // 模糊查询，模糊错误范围在 0，1，2。搜索关键词长度必须大于2才允许有模糊错误
        // 搜索关键词长度为2  不允许模糊错误范围
        // 搜索关键词长度为3~5  允许 0，1 次的模糊错误
        // 搜索关键词长度大于5  允许 0，1,2 次的模糊错误
        QueryBuilders.fuzzyQuery("intro", "测试");
        // 多字段匹配查询
        // 如果搜索的字段（field）分词     会对 text 先进行分词 再搜索
        // 如果搜索的字段（field）不分词   会直接使用 text 整体 进行查询
        QueryBuilders.multiMatchQuery("测试", "username","intro");
        // 多字段分词查询 会将查询条件进行分词再去查询，也可以指定分词器
        QueryBuilders.queryStringQuery("");

        // 过滤存在字段的  搜索指定索引中存在 某个字段名的
        QueryBuilders.existsQuery("字段名");

        queryBuilder.withQuery(bool);
//        PageRequest of = PageRequest.of(0, 10);

        // 按照年龄升序排列
        queryBuilder.withSort(SortBuilders.fieldSort("age").order(SortOrder.ASC));
        // 分页查询                             页数从0开始，每页查询8条
        queryBuilder.withPageable(PageRequest.of(0, 8));
        // 过滤字段。此处只保留 username和age两个字段
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"username","age"},null));
        // 构造查询条件
        NativeSearchQuery build = queryBuilder.build();
        // 分页查询
        Page<Book> page = bookRepository.search(build);
        System.out.println("总页数："+page.getTotalPages());
        System.out.println("总数据条数："+page.getTotalElements());
        for (Book book : page) {
            System.out.println(book);
        }

    }


}
